home *** CD-ROM | disk | FTP | other *** search
/ 8bitfiles.net/archives / archives.tar / archives / compuserve-file-archive / 05 Programming / BFASM.DOC < prev    next >
Text File  |  2019-04-13  |  29KB  |  596 lines

  1. @                         Using the Forth Assembler
  2.  
  3. What this file is:
  4.    A lot of the mail I have been getting about Blazin' Forth concerns the
  5. use of CODE and ;CODE definitions, and combining assembly language with
  6. Forth.  It recently occurred to me that a lot of people don't know how to
  7. use the Forth assembler - and are therefore missing out on a considerable
  8. amount of the power of the Forth language. So this is an attempt at a
  9. tutorial in using Assembler in Forth, and in using it in the BFC in
  10. particular.  Much of what I say here should be applicable to other 6502
  11. Forth implementations - but not necessarily all. Consider yourself warned.
  12.  
  13. What this file is not:
  14.    This is *not* a course in machine language programming.  If you need
  15. information on the basics of using assembly language, then consult a good
  16. text - my personal favorites are the ones by Lance Leventhal, but there are
  17. many good ones around.
  18.  
  19. General Considerations:
  20.    Using CODE and ;CODE words are frowned upon in programs which attempt to
  21. be portable.  In fact, the quickest way to insure that your programs are not
  22. 83 Standard is to use the word CODE in them.
  23.    Having said this however, there is much to be said for incorporating a
  24. few code definitions in Forth programs.  Typically, a program will spend
  25. most of its time in one or two words - by recoding these words in assembler,
  26. the overall speed of the program can be increased manyfold - 50% increases
  27. or more are not unusual, and for very little work.
  28.    Also, the incompatibility is not as great as it would appear at first.
  29. It is probably safe to say that the most popular 8-bit personal computers
  30. use the 6502 CPU (Apple II, C64 C128, Atari line etc.). As long as your CODE
  31. definitions are not accessing special hardware features (such as the SID
  32. chip in the C64) most code definitions will work on all computers using the
  33. same CPU. I have shared CODE definitions with friends who have Apples and
  34. Ataris running Forth, and I have never yet had to modify one.
  35.    Finally, Forth is the easiest language there is to combine with machine
  36. language.  Many of the hassles and problems ordinarily associated with
  37. combining high level languages and assembler simply do not occur in Forth.
  38. In other languages, the typical process is this:
  39.    1: Write the high level program, and debug.
  40.    2: Figure out where you need to speed things up.
  41.    3: Fire up an editor, and write some assembly code.
  42.    4: Assemble the file and - usually - run a loader program on that output.
  43.    5: Load the Editor, and modify your higher level source.
  44.    6: Compile your higher level source.
  45.    7: Link the compiled program and the loaded assembly language program.
  46.    8: When it doesn't work (which it won't, at first). Go back to step 3.
  47.  
  48.    There are also inevitable problems in passing parameters to and from the
  49. higher level code, and the inevitable problem of where to put the machine
  50. language. If you have ever done much of this sort of thing, you know what a
  51. headache these problems can be.
  52.    These last two problems are usually the most difficult, and, you will be
  53. surprised, and possibly relieved to hear that they don't occur in Forth. At
  54. all. Ever.
  55.    Combine that with a resident assembler and a resident editor and a
  56. resident compiler, writing Assembly language in Forth becomes almost
  57. ridiculously easy.
  58.  
  59. A Quick Example:
  60.    Since I have taken up so much of your time with the above advertisement,
  61. you would probably like me to put my money where my mouth is. Here is a
  62. quick example of what I was talking about above.  I hope it's not too
  63. trivial, but the idea I want to get across here is the ease of combining
  64. Assembler with Forth. First, a high level Forth Program:
  65.  
  66. : SHIFTLEFT ( -- N2 N1 ) // shift N2 left N1 times
  67.       0 ?DO  2* LOOP ;
  68.  
  69. : SHIFT-4 ( Just show what a left shift is )
  70.       100 0 DO  I 4 SHIFTLEFT .  LOOP ;
  71.  
  72. Multiple left shifts are fairly common, and so SHIFTLEFT is likely to be a
  73. handy word in certain applications.  As it is, it will run pretty quickly.
  74. But perhaps, for certain speed demons, not quickly enough, so you decide to
  75. recode the SHIFTLEFT primitive in Assembler:
  76.  
  77. CODE SHIFTLEFT ( -- N2 N1 )  // shift N2 left N1 times
  78.    BOT LDA, TAY, BEGIN, 0 # CPY, 0= NOT  WHILE,
  79.       SEC ASL, SEC 1+ ROL, DEY, REPEAT,   POP JMP, END-CODE
  80.  
  81. If you are used to conventional assemblers, this probably looks pretty
  82. weird.  The important thing to notice here is that *only* SHIFTLEFT has
  83. changed - SHIFT-4 (or any other word which uses SHIFTLEFT) will work just as
  84. it did before, with the only change being the overall increase of speed
  85. which machine language naturally brings to any situation.  Notice also that
  86. we didn't have to worry about where to put the code - it goes in the same
  87. spot our higher level SHIFTLEFT went. You will also discover, if you type
  88. this example in, that you don't have to call the assembler. This is all
  89. taken care of for you.  As far as you, another user, or other procedures
  90. which use SHIFTLEFT are concerned, there is no difference between using the
  91. hi-level SHIFTLEFT and the CODE SHIFTLEFT.
  92.  
  93. The Structure of a CODE definition:
  94.    It's pretty straightforward. They all look the same:
  95.  
  96. CODE [name] [assembler mnemonics] END-CODE
  97.  
  98. Note the similarity to a colon definition:
  99.  
  100. : [name]    [forth words]         ;
  101.  
  102. How to Exit a Code Definition:
  103.    One thing you must remember is that you have to explicitly leave a CODE
  104. definition by doing a JMP, to another code level routine.  This is probably
  105. the single most common error made by newcomers.  Possibly it is caused by
  106. making a false analogy between the higher level ; and the code level
  107. END-CODE. While the Forth word ; does in fact get you back to where you came
  108. from, END-CODE does not. In fact, END-CODE does nothing at all at run-time.
  109. You can exit a CODE definition by doing a JMP, to any of the following:
  110. NEXT POP POPTWO PUSH or PUT .  These are described below:
  111.  
  112. NEXT
  113.    NEXT is commonly called the address interpreter. It is the word that is
  114. responsible for the execution of all Forth words.  ALL words in Forth
  115. ultimately end up here.  Doing a NEXT JMP, will cause the current code
  116. definition to stop and return to the word that called it.  All of the
  117. following exit points end with a jump to NEXT .  In what follows, remember
  118. that a "stack element" refers to a 16 bit quantity -- i.e. two bytes.
  119.  
  120. POP
  121.    POP first drops the first element of the stack, and then jumps to NEXT.
  122. Same as DROP in hi-level forth.
  123.  
  124. POPTWO
  125.    POPTWO drops the top two elements from the stack, and then jumps to NEXT.
  126. Same as 2DROP in hi-level.
  127.  
  128. PUSH
  129.    PUSH lets you leave a result on the top of the stack.  PUSH expects the
  130. low byte of the new top of stack to be on the return stack, and the high
  131. in the accumulator. PUSH will leave these as the new top of the parameter
  132. stack (the former top will then be the second element). PUSH then calls
  133. NEXT. Since this routine is somewhat more complicated than the others here
  134. is a typical sequence:
  135.  
  136.       PHA, ( push low byte to return stack )
  137.       TYA, ( assume high byte is saved in Y register, move it to the A reg)
  138.       PUSH JMP, END-CODE ( parameters set, so jump to PUSH)
  139.  
  140. PUT
  141.    PUT replaces the top of the stack with a new value.  You setup PUT in
  142. the same way as you setup for PUSH - the new lobyte is pushed to the return
  143. stack, and the new hibyte is in the accumulator before the call to PUT. The
  144. only difference between the two is that PUT replaces the present top of the
  145. stack, while PUSH creates a new top of stack. 
  146.  
  147. Register Usage:
  148.    Since Forth uses two stacks, and the 6502 CPU only implements one
  149. hardware stack, the parameter stack must be "artificially" maintained. In
  150. this implementation (as in most) it is located in the zero page, and the X
  151. register is used as the parameter stack pointer. The machine stack is
  152. Forth's return stack. Therefore instructions which affect the X register or
  153. the machine stack should be used with care.  At entry to a CODE defintion,
  154. the X register points to the top byte of the parameter stack.  This stack
  155. grows downward in memory, so decrementing the X register will make room
  156. for another element on the stack, and incrementing the stack pointer will
  157. remove an element from the stack. Here is a diagram which shows the
  158. situation when two elements are on the stack:
  159.  
  160.          Hi Memory
  161.       **************
  162.       *   hibyte2  *
  163.       *   lobyte2  *
  164.       **************
  165.       *   hibyte1  *
  166. X --> *   lobyte1  * Top of Stack
  167.       **************
  168.  
  169. Notice that the two bytes which make up one stack entry are stored in the
  170. usual 6502 order, with the lobyte lower in memory. To remove the top element
  171. of the stack, we can define the code word DROP:
  172.  
  173. CODE DROP ( N -- )  INX, INX, NEXT JMP, END-CODE
  174.  
  175. Which in fact, is exactly the the way DROP is defined in the BFC. After DROP
  176. has been executed, the stack looks like this:
  177.  
  178.         Hi Memory
  179.       *************
  180.       *  hibyte2  *
  181. X --> *  lobyte2  * New top of Stack
  182.       *************
  183.  
  184. We will return to this topic later, when we talk about accessing the
  185. parameter stack in more detail.  For now, the main point is to remember that
  186. when Forth starts executing your code definition, the X register will
  187. contain a pointer to the top of the stack. You can use the X register to
  188. access the stack, or to remove elements from the stack, but when your code
  189. definition is finished, other Forth words, and the Forth system itself is
  190. going to expect the X register to contain a valid stack pointer, so don't
  191. change it wantonly. You should also remember that since each stack entry is
  192. two bytes, only even multiples of the INX, or DEX, instruction make sense.
  193. (I.E. INX, INX, INX, INX, not INX, INX, INX, - the first will drop two stack
  194. elements, while the second will drop 1 and 1/2 stack elements - and cause
  195. the Forth system to behave oddly.)
  196.  
  197. Both the Accumulator (A reg) and the Y register may be freely used. The A
  198. register will contain garbage, and must be initialized, but the Y register
  199. is guaranteed to be 0 on entry to your code, and you may take advantage of
  200. this fact or not, as you wish.  Here is a short code definition that will
  201. leave the value of -1 (Forth's canonical TRUE flag) on the stack. It uses
  202. the PUSH routine described earlier.
  203.  
  204. CODE TRUE ( -- -1 )   DEY, ( Y REG now holds $ff)
  205.                       TYA, PHA, PUSH JMP, END-CODE
  206.  
  207. Here is an even shorter definition which will leave a 0 on the parameter
  208. stack:
  209.  
  210. CODE FALSE ( -- 0 )  TYA, ( set A register to 0 )
  211.                      PHA, ( set up for PUSH )
  212.                      PUSH JMP, END-CODE
  213.  
  214. Life being what it is, you will often wish you could use the X register.
  215. There is a way.  You can use the system storage location XSAVE, to
  216. temporarily save the value of the X register while you are doing other
  217. things. You must remember to restore the X register before exiting, however.
  218. A typical sequence is:
  219.  
  220.   XSAVE STX, ( stuff that changes x ) XSAVE LDX, NEXT JMP, END-CODE
  221.  
  222.  
  223. Why it looks so strange....
  224.  
  225.    The main reason the Forth Assembler looks so strange is that it is
  226. reverse polish, like all of Forth. Operands *preceed* the operators. Here
  227. are some examples that should make it clear:
  228.  
  229. Conventional Assembler           Forth's Assembler
  230. =====================            ================
  231.    LDA # 0                          0 # LDA,
  232.    ROL A                            .A  ROL,
  233.    STA ADDRESS,X                    ADDRESS ,X STA,
  234.    STA (ADDRESS,X)                  ADDRESS X) STA,
  235.    LDA (ADDRESS),Y                  ADDRESS )Y LDA,
  236.    JMP (INDIRECT)                   INDIRECT ) JMP,
  237.    JMP ADDRESS                      ADDRESS  JMP,
  238.    LDA ADDRESS                      ADDRESS  LDA,
  239.  
  240. While admittedly unusual, it does make the best use of the stack at assembly
  241. time.
  242.  
  243. The other major difference is that the Forth Assembler does not use labels.
  244. There are no branch instructions - the Forth Assembler uses a structured
  245. code approach to control flow. It does this by using analogues of the
  246. hi-level IF THEN ELSE etc. to control the flow of your CODE definition. To
  247. take advantage of this, you must specify the condition code you want tested.
  248. You specify this condition code by using any of the following words:
  249.  
  250.    CS  test if carry set
  251.    0<  test if negative flag set
  252.    0=  test if zero flag set
  253.    VS  test if overflow flag set
  254.  
  255.    You can follow these condition code specifiers with not, to test for the
  256. opposite condition:
  257.  
  258.    CS NOT test if carry clear
  259.    0< NOT test if negative clear
  260.    0= NOT test if zero flag clear
  261.    VS NOT test if overflow flag clear
  262.  
  263. Below is an example of a possible definition of 0= , which leaves true if
  264. the top of the stack is 0, and false (0) if it is anything else:
  265.  
  266. CODE 0= ( N -- FLAG ) BOT LDA, BOT 1+ ORA, 0= IF, 255 # LDA, ELSE, 0 # LDA,
  267. THEN, PHA, PUT JMP, END-CODE
  268.  
  269. In the above code, we first test for 0 by ORing the two bytes which make up
  270. the top of the stack together. The result will be zero only if both are
  271. zero. We then test the zero flag (with 0=). If the byte is 0, we LDA with
  272. 255, otherwise, we LDA with 0, and replace the top of the stack with the
  273. flag by jumping to the PUT exit routine. Note that we could make this
  274. definition much shorter by taking advantage of the fact that the Y register
  275. is zero at entry:
  276.  
  277. CODE 0= ( N -- FLAG ) BOT LDA, BOT 1+ ORA, 0= IF, DEY, THEN, TYA, PHA, PUT
  278. JMP, END-CODE
  279.  
  280. You can use the same type of tests to do conditional loops. Here is a do
  281. nothing example that simply wastes some time in a loop:
  282.  
  283. CODE WAIT ( -- ) BEGIN, DEY, 0= UNTIL, NEXT JMP, END-CODE
  284.  
  285. This simply decrements the Y register until it becomes zero.  In the
  286. original Forth assembler for 6502 machines, the BEGIN, UNTIL, structure was
  287. the only one available. The BFC has extended this to include BEGIN, WHILE,
  288. REPEAT, and BEGIN, AGAIN, . The BEGIN, AGAIN, loop is infinite - you must
  289. JUMP out of it in the middle somewhere.
  290.  
  291. Accessing the Stacks.
  292.  
  293.    Typically, most routines only need to access the top two elements of the
  294. parameter stack. Since this is so common, special words have been provided
  295. to make life easier here. BOT references the top of the stack (which is
  296. lower in memory, and so the BOTtom of the stack). SEC references the SECond
  297. element of the stack. It's important to remember that a Forth stack entry is
  298. 16 bits, or two bytes, so to obtain the whole stack element, you need to do
  299. two fetches or stores. Here is a sample implementation of DUP:
  300.  
  301. CODE DUP   ( N -- N N ) BOT LDA, PHA, BOT 1+ LDA, PUSH JMP, END-CODE
  302.  
  303. First we fetch the low byte of the top of the stack with BOT LDA, . This is
  304. pushed onto the return stack, as required by the exit routine PUSH . Next we
  305. get the high byte with BOT 1+ LDA, . That's all there is to it. As another
  306. illustration, here is an implementation of OVER:
  307.  
  308. CODE OVER ( A B -- A B A ) SEC LDA, PHA, SEC 1+ LDA, PUSH JMP, END-CODE
  309.  
  310. The actual addressing mode being used here is "zero-page x". In
  311. conventional assembler, BOT LDA, would be written LDA 0,x while BOT 1+ LDA,
  312. would be written LDA 1,X . To access deeper stack elements, you can keep
  313. adding values to BOT or SEC , or you can use the addressing mode explicitly:
  314.  
  315. BOT 4 + LDA
  316. or
  317. 4 ,X LDA
  318.  
  319. While not used often, it is also possible to address directly into the
  320. return stack. Typically, you would access the return stack using the PLA,
  321. instruction. This has the side effect of altering the stack pointer, and you
  322. can also only access the top of the return stack.  To access arbitrary
  323. bytes, you can use RP) . To do this, you must first save the X-Register in
  324. XSAVE and then execute TSX, which will move the stack pointer into the
  325. X-Register. You can then do RP) LDA, which will fetch the current top of the
  326. return stack. To get deeper into the return stack, offset RP). Below is an
  327. example which non-destructively moves the address on the return stack to the
  328. top of the parameter stack:
  329.  
  330. CODE GET-RETURN-ADDRESS  XSAVE STX,  TSX, RP) LDA, PHA, RP) 1+ LDA, XSAVE
  331. LDX, PUSH JMP, END-CODE
  332.  
  333. One of the easiest and quickest ways to crash any system is to garbage the
  334. return stack. If you need to access the stack, go for it, but use care.
  335.  
  336. SETUP and N
  337.    It is often useful to be able to access absolute memory locations.  To
  338. this end, Forth provides an 8 byte temporary data area which is referred to
  339. as the N area. You may initialize this area yourself, or you may call SETUP
  340. to move stack elements to the N area. To use SETUP, you must load the
  341. accumulator with the number of stack elements you want to move (NOTE: the
  342. number of stack elements, NOT the number of bytes) and then do a JSR to
  343. SETUP. SETUP will pop the elements off of the stack, and move them to the N
  344. area.  Since there are only 8 bytes in the N area, you have room for at most
  345. 4 stack elements. As a simple example, the following will pop the top two
  346. elements off of the stack, and move them to the N area:
  347.  
  348. CODE MOVE2  2 # LDA, SETUP JSR, END-CODE
  349.  
  350. The previous top of stack (BOT and BOT 1+) will be stored at N and N 1+ ,
  351. while the second element (SEC and SEC 1+) will be at N 2+ and N 3 +. Once
  352. they have been moved there, you can carry out operations on them, or use
  353. them in the indexed indirect Y addressing mode. ( LDA (N), Y in conventional
  354. assembler, or N )Y LDA, in Forth assembler.)  By far the most common error
  355. in using SETUP is to forget that it also pops the elements of the stack as
  356. it moves them.
  357.  
  358. ;CODE
  359.  
  360. If CODE is the Assemblers equivalent to the hi-level colon, ;CODE is the
  361. assemblers equivalent to DOES>. As an example of the use of ;CODE, we will
  362. write our own versions of CONSTANT - one in high level forth, using DOES>,
  363. and one in low level, using ;CODE. First the hi-level definition:
  364.  
  365. : CONSTANT  CREATE , DOES> @ ;
  366.  
  367. As a quick review, remember that CREATE (or a word that uses CREATE) creates
  368. a dictionary entry for the next word in the input stream. Words created in
  369. this way all have the same run-time behaviour - they leave the address of
  370. their parameter field on the stack. Note that CREATE by itself does not
  371. allocate any parameter field space in the dictionary, you must do this
  372. yourself by using C, , or ALLOT . The DOES> word allows you to manipulate
  373. the values in the parameter field, essentially allowing you to define a
  374. special set of related words which share the same run-time behaviour. Lets
  375. walk through the process:
  376.  
  377. 10 CONSTANT TEN
  378.  
  379. When Forth executes the above line, the number 10 will be left on the stack,
  380. and CONSTANT will be executed. The first word in CONSTANT is CREATE, which
  381. will take the first word it finds in the input stream (TEN in this case) and
  382. create a header for it in the dictionary, with its associated link field,
  383. name field and code field. The code field written by CREATE will cause the
  384. address of the first byte of the parameter field to be left on the stack
  385. when TEN (the word defined by CONSTANT) is executed. Next , is executed,
  386. which takes the top of the stack, and compiles this number in the
  387. dictionary. DOES> does something mysterious (more on this later) which
  388. causes the words following DOES> to be executed at run-time. This is the
  389. compile time behaviour of CONSTANT. Now, when TEN is executed, the run-time
  390. behaviour of CONSTANT will occur. The parameter fields address is pushed
  391. onto the stack, and then the words after DOES> are executed. In this case,
  392. the single word @ will be executed, which replaces the address on the top of
  393. the stack with the value at that address.
  394.  
  395. TEN . 10 OK
  396.  
  397. Now for the ;CODE version:
  398.  
  399. : CONSTANT  CREATE , ;CODE  2 # LDY, W )Y LDA, PHA, INY, W )Y LDA, PUSH JMP,
  400. END-CODE
  401.  
  402. Notice, by the way, that no concluding semi-colon is required when you use
  403. ;CODE.
  404.  
  405. When ;CODE is executed (for example, by typing 10 CONSTANT TEN), it will
  406. REWRITE THE CODE FIELD OF THE WORD BEING DEFINED! This is extremely
  407. important to remember.  All Forth words have code fields that define their
  408. run time behaviour, with colon definitions all sharing the same code field,
  409. variables sharing the same code field (different, of course, from that of
  410. colon definitions and constants) and so on. When CREATE executes in our
  411. CONSTANT definition, it creates a code field that contains the address of a
  412. routine which will push the address of the word being defined to the stack.
  413. When ;CODE executes in our CONSTANT definition, it will search out this code
  414. field, and re-write it with the address of the machine language routine
  415. which immediately follows ;CODE. This means that the only run time behaviour
  416. of a word which contains a ;CODE termination is determined by you. Even
  417. though there is a CREATE in the lo-level definition of CONSTANT, it will not
  418. deposit the address of the parameter field on the stack, because ;CODE has
  419. changed the code field to point to our machine language routine. An
  420. illustration may make this clearer:
  421.  
  422. Header created by CREATE:
  423. **************
  424. * Link Field *
  425. **************
  426. * TEN        *  Name field - stores ASCII characters of defined name.
  427. **************
  428. *  $0900     *  $0900 = address of routine to push parameter field on stack
  429. **************
  430. * 10         *  Parameter field - stores value of constant
  431. **************
  432.  
  433. Note that $0900 is just an example address - don't try jumping to it in your
  434. own code.
  435.  
  436. When a word defined by CREATE is executed, the machine language routine at
  437. $0900 (the address in the code field of the word) will be executed. This
  438. routine is what causes the run-time behaviour of CREATE defined words - it
  439. pushes the address of the parameter field to the stack. The above
  440. illustration shows the state of the dictionary after the CREATE and , part
  441. of CONSTANT have been executed. Now, suppose that the address of the first
  442. instruction following ;CODE in CONSTANT ( the 2 # LDY, ) is at $8000 . When
  443. ;CODE executes in the course of defining a new constant, the header will
  444. look like this:
  445.  
  446. Header after ;CODE has been executed.
  447. **************
  448. * Link Field *
  449. **************
  450. * TEN        *  Name field - stores ASCII characters of defined name.
  451. **************
  452. * $8000      *  $8000 = address of your machine language routine
  453. **************
  454. * 10         *  Parameter field - stores value of constant
  455. **************
  456.  
  457. So, as always, when Forth executes TEN, the first thing to be executed is
  458. the address of the routine in the code field, which no longer points to the
  459. CREATE run time code, but to your machine language code.
  460.  
  461. Hope that is all clear enough. Now, to the particulars of the ;CODE part of
  462. our low level CONSTANT definition (note that the following is implementation
  463. dependent. Most present day Forths use an implementation along these lines,
  464. but not all.)
  465.  
  466. Forth depends on two registers for its performance, the IP and the W
  467. register. When Forth is implemented on processors with more registers,
  468. typically a processor register will be used for these Forth system
  469. registers. However, there are not enough on the 6502, so zero page locations
  470. have been used instead.  W will contain the address of the code field of the
  471. word currently being executed. For example, if the code field of TEN is
  472. located at $9000, the W register will contain $9000, or the address of the
  473. address of the routine to be executed. The ;CODE part of CONSTANT uses this
  474. fact to access the parameter field of the current word. Since the code field
  475. of an address, is always two bytes long, the first byte of the parameter
  476. field is located at $9002. Given this information, it becomes a simple
  477. matter of indirectly indexing from W. Loading the Y register with 2, and
  478. then performing W )Y LDA, (LDA (W),Y in conventional assembler) will get the
  479. first byte of the constants value. The next byte is at $9003, so we simply
  480. increment Y, and indirectly index again to get the next byte. The rest of
  481. the definition consists of setting up for PUSH, which was covered earlier.
  482.  
  483. Phew! Hope that was clear enough. I think you can see that indexing from W
  484. can get you any byte in a definitions parameter field. W 1- contains an
  485. indirect JMP instruction, so you can also vector control to other routines
  486. by storing the address of the address of the routine in W, and then doing a
  487. W 1- JMP, . This technique is rarely used, however.
  488.  
  489. There is an intimate relationship between the IP register and the W
  490. register, and an effective use of these registers depends on a clear
  491. understanding of how they work in the Forth system as a whole.  Both of them
  492. are changed by NEXT. When NEXT executes, the IP will point to an address
  493. which contains the code field address of the next word to be executed by
  494. Forth. NEXT fetches this address, and stores it in W. It then bumps the IP
  495. (which stands for Interpretive Pointer) by two, so the next time around it
  496. will be pointing to the next word to be executed. Finally, NEXT does a JMP
  497. to W 1-, which causes an indirect jump to the address pointed to by W.
  498. The IP is most useful for accessing things like inline data structures,
  499. (character strings, and the like.) Since the IP is incremented by NEXT, the
  500. code fragment:
  501.     IP )Y LDA, PHA, INY, IP )Y LDA, PUSH JMP,
  502.  
  503. Will push the address which contains the address of the next Forth word to
  504. be executed to the parameter stack. (This will be the third and fourth bytes
  505. past the address of the current word being interpreted.) Obviously, by
  506. storing new values in the IP, and then jumping to NEXT , you can also force
  507. the execution of a particular definition.  This technique is also rarely
  508. used. Typical uses for the IP are mainly accessing inline data.
  509.  
  510. Accessing Variables from Code definitions.
  511.    Since variables leave the address of their parameter fields on the stack,
  512. it is a simple matter to access these variables from CODE level definitions.
  513. Here is an example:
  514.  
  515. VARIABLE FOO
  516.  
  517. CODE FOO+1   FOO LDA, CLC, 1 # ADC, FOO STA, CS IF, FOO 1+ INC, THEN
  518. END-CODE
  519.  
  520. This is a common way to increment the value at a memory location.  The
  521. assembler also provides a way to access USER variables, using the UP (user
  522. pointer constant). User variables must be accessed using an offset, so the
  523. )Y addressing mode  is recommended. You will need to know the offset of the
  524. user variable you need to access in advance, of course.
  525.  
  526. MISC.
  527.  
  528.    Since the Assembler is co-resident with the FORTH system, all the power of
  529. FORTH is available to you when using the Assembler. As one example:
  530.  
  531.    FOO @ 2 3 */ # LDA,
  532.  
  533. will initialize the accumulator to two thirds of the value stored in the
  534. variable FOO. (Of course, this must be 255 or less.) Occasional conflicts
  535. may arise, however. In particular, a common error is to confuse the
  536. assemblers 0= and 0< words with Forths - they are not the same. If you wish
  537. to use the Forth versions while assembling, you must explicitly enter the
  538. FORTH vocabulary, do your Forth thing, and then re-enter the assembler
  539. vocabulary. Such conflicts are rare, and usually easily recognized.
  540.  
  541. The Forth assembler uses the standard MOS mnemonics for the 6502 op-codes,
  542. but each mnemonic has a ',' attached. Thus, in forth, we write LDA, BRK, or
  543. JMP, not LDA BRK and JMP. Also, the Assembler conditionals use the same
  544. convention - IF, ELSE, THEN, and not IF ELSE THEN . A common error is to
  545. omit the comma from one or more of these conditionals.  I am personally not
  546. wild about this convention, but the first Forth assemblers used them, and
  547. now we are stuck with it.
  548.  
  549. You may have noticed that I have not used the JSR, RTS, instructions. This
  550. is because typically, CODE definitions are called from higher level words,
  551. and must end with a JMP, to NEXT or the equivalent routine. It is possible
  552. to write programs which are structured in this way. Typically, the
  553. subroutines are given names with CREATE, and then called from a higher level
  554. code definition. Here is an example:
  555.  
  556. CREATE BLETCH  ASSEMBLER  NOP, NOP, RTS,
  557.  
  558. Note that when we do this, we must invoke the Assembler vocabulary
  559. explicitly. BLETCH would be typically called from a higher level CODE
  560. definition:
  561.  
  562. CODE THE-GREAT-RTS-HACK  BLETCH JSR, NEXT JMP, END-CODE
  563.  
  564. This is only occasionally necessary however - usually when writing code that
  565. is extremely time critical, such as graphics code.
  566.  
  567. Finally, it is possible to exit a code definition by jumping to yet another
  568. code routine. You must remember to restore the X register (if you have
  569. altered it) and it is also wise to reset the Y register to 0, since many
  570. code level definitions will assume that this is the case. As a simple
  571. example, here is a word that simply simply does a JMP, to Forth's KEY
  572. routine:
  573.  
  574. CODE CALL-KEY  ' KEY @ JMP, END-CODE
  575.  
  576. The tick (') gets the address of KEY's code field, and the @ fetches the
  577. address of the routine which is stored in KEY's code field. This is the
  578. recommended technique. It is more portable, and also safer than others which
  579. one sees.  Note that you cannot call hi-level Forth words using this
  580. technique - only code level definitions may be called in this way. If you
  581. think you want to exit a CODE definition with a call to a higher level Forth
  582. word, think again. If you still need to do it, then start tinkering with the
  583. IP and W registers.
  584.  
  585. I think that about covers it. I have tried to cover all of the basics, and
  586. many of the more advanced techniques in combining assembly language and
  587. Forth. If some of it seems obscure, it is probably my explanation, since
  588. hacking in CODE is really no more difficult than hacking in any language -
  589. it just runs faster.
  590.  
  591. Good Luck
  592. And
  593. Happy Hacking!
  594. SDB
  595.  
  596.